package stardust.core.support.db;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


/**
 * JDBC 数据访问类
 * <p>
 * 对 JDBC 数据操作提供了通用的支持。 <br>
 * 注意：指令执行完毕后数据连接并不会被释放。
 * <p/>
 * @author 刘晓园
 */
public class DataAccessObject
{

  /**
   * 数据连接
   */
  private Connection connection;

  /**
   * 默认的构造器
   */
  public DataAccessObject()
  {
  }

  /**
   * 通过数据连接初始化
   * <p/>
   * @param connection
* 可用的数据连接
   */
  public DataAccessObject(Connection connection)
  {
    this();
    try
    {
      if (connection == null)
      {
        throw new NullPointerException("没有提供数据连接。");
      }
      else if (connection.isClosed())
      {
        throw new IllegalStateException("数据连接已经关闭。");
      }

      this.connection = connection;
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
  }

  /**
   * 获取数据连接
   * <p/>
   * @return 如果有将返回当前使用的数据连接
   */
  public Connection getConnection()
  {
    return this.connection;
  }

  /**
   * 设置数据连接
   * <p/>
   * @param connection
* 数据连接
   */
  public void setConnection(Connection connection)
  {
    this.connection = connection;
  }

  /**
   * 执行 SQL 指令
   * <p>
   * 执行不包含查询的 SQL 指令。 <br>
   * 注意：指令执行完毕后数据连接并不会被释放。
   * <p/>
   * @param sql
 * 指令
   * @param args
* 指令参数
   * <p/>
   * @return 指令所影响的行数
   */
  public int execute(String sql, Object... args)
  {
    PreparedStatement pst = null;

    try
    {

      pst = this.connection.prepareStatement(sql);

      for (int i = 0; args != null && i < args.length; i++)
      {
        pst.setObject(i + 1, args[i]);
      }
      return pst.executeUpdate();
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
    finally
    {
      try
      {
        if (pst != null)
        {
          pst.close();
        }
      }
      catch(Exception e)
      {
        throw new RuntimeException(e);
      }
    }
  }

  /**
   * 执行 SQL 指令
   * <p>
   * 执行查询 SQL 指令。 <br>
   * 注意：指令执行完毕后数据连接并不会被释放。
   * <p/>
   * @param sql
   * 查询指令
   * @param args
   * 指令参数
   * <p/>
   * @return 返回查询后经过封装的结果
   */
  public List<Map<String, Object>> query(String sql, Callback cb, Object... args)
  {
    PreparedStatement pst = null;
    ResultSet rs = null;
    ResultSetMetaData rsMeta = null;

    try
    {

      pst = this.connection.prepareStatement(sql);

      for (int i = 0; i < args.length; i++)
      {
        pst.setObject(i + 1, args[i]);
      }

      rs = pst.executeQuery();
      rsMeta = rs.getMetaData();

      List<Map<String, Object>> dataList = new LinkedList<Map<String, Object>>();
      Map<String, Object> data;

      while (rs.next())
      {
        data = new HashMap<String, Object>();

        for (int i = 0; i < rsMeta.getColumnCount(); i++)
        {
          data.put(rsMeta.getColumnLabel(i + 1), rs.getObject(i + 1));
        }

        if (cb != null)
        {
          cb.callback(data);
        }
        else
        {
          dataList.add(data);
        }
      }

      return dataList;
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
    finally
    {
      try
      {
        if (pst != null)
        {
          pst.close();
        }
      }
      catch(Exception e)
      {
        throw new RuntimeException(e);
      }
    }
  }
  
  /**
   * 执行 SQL 指令
   * <p>
   * 执行查询 SQL 指令。 <br>
   * 注意：指令执行完毕后数据连接并不会被释放。
   * <p/>
   * @param sql
   * 查询指令
   * @param args
   * 指令参数
   * <p/>
   * @return 返回查询后经过封装的结果
   */
  public List<Map<String, Object>> query(String sql, Object... args)
  {
    PreparedStatement pst = null;
    ResultSet rs = null;
    ResultSetMetaData rsMeta = null;

    try
    {

      pst = this.connection.prepareStatement(sql);

      for (int i = 0; i < args.length; i++)
      {
        pst.setObject(i + 1, args[i]);
      }

      rs = pst.executeQuery();
      rsMeta = rs.getMetaData();

      List<Map<String, Object>> dataList = new LinkedList<Map<String, Object>>();
      Map<String, Object> data;

      while (rs.next())
      {
        data = new HashMap<String, Object>();

        for (int i = 0; i < rsMeta.getColumnCount(); i++)
        {
          data.put(rsMeta.getColumnLabel(i + 1), rs.getObject(i + 1));
        }

         dataList.add(data);
      }

      return dataList;
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
    finally
    {
      try
      {
        if (pst != null)
        {
          pst.close();
        }
      }
      catch(Exception e)
      {
        throw new RuntimeException(e);
      }
    }
  }

  public Map<String, Object> get(String sql, Object... args)
  {
    List<Map<String, Object>> list = this.query(sql, null, args);

    return list.size() == 0 ? null : list.get(0);
  }

  public Long getSequence(String seq)
  {
    Map<String, Object> seqData = this.get("SELECT " + seq + ".NEXTVAL SN FROM DUAL");

    if (seqData == null)
    {
      return null;
    }
    else
    {
      return Long.parseLong(seqData.get("SN").toString());
    }
  }
  
  public void commit()
  {
    try
    {
      this.connection.commit();
    }
    catch (SQLException e)
    {
      throw new RuntimeException(e);
    }
  }
  
  public void rollback()
  {
    try
    {
      this.connection.rollback();
    }
    catch (SQLException e)
    {
      throw new RuntimeException(e);
    }
  }

  public void close(boolean rollback)
  {
    try
    {
      if (this.connection != null && !this.connection.isClosed())
      {
        try
        {
          if (rollback)
          {
            this.connection.rollback();
          }
          else
          {
            this.connection.commit();
          }

          this.connection.close();
        }
        catch(SQLException e)
        {
          ;
        }
      }
    }
    catch(SQLException e)
    {
      ;
    }

  }

  public static interface Callback
  {

    void callback(Map<String, Object> row) throws Exception;

  }

}
